# StalactiteRepositoryFactory.java

    package org.codefilarete.stalactite.spring.repository;

    import javax.annotation.Nullable;
    import java.lang.reflect.Method;
    import java.util.Optional;

    import org.codefilarete.stalactite.engine.runtime.AdvancedEntityPersister;
    import org.codefilarete.stalactite.spring.repository.query.CreateQueryLookupStrategy;
    import org.codefilarete.stalactite.spring.repository.query.bean.BeanQueryLookupStrategy;
    import org.codefilarete.stalactite.spring.repository.query.nativ.NativeQueryLookupStrategy;
    import org.codefilarete.stalactite.sql.ConnectionProvider;
    import org.codefilarete.stalactite.sql.Dialect;
    import org.codefilarete.tool.bean.Objects;
    import org.codefilarete.tool.collection.Arrays;
    import org.codefilarete.tool.collection.Iterables;
    import org.codefilarete.tool.exception.NotImplementedException;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.ListableBeanFactory;
    import org.springframework.data.projection.ProjectionFactory;
    import org.springframework.data.repository.core.EntityInformation;
    import org.springframework.data.repository.core.NamedQueries;
    import org.springframework.data.repository.core.RepositoryInformation;
    import org.springframework.data.repository.core.RepositoryMetadata;
    import org.springframework.data.repository.core.support.RepositoryFactorySupport;
    import org.springframework.data.repository.query.QueryLookupStrategy;
    import org.springframework.data.repository.query.QueryLookupStrategy.Key;
    import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
    import org.springframework.data.repository.query.RepositoryQuery;

    /**
     * Mimics JpaRepositoryFactory for Stalactite
     *
     * @author Guillaume Mary
     */
    public class StalactiteRepositoryFactory extends RepositoryFactorySupport {

    	private final AdvancedEntityPersister<?, ?> entityPersister;
    	private final Dialect dialect;
    	private final ConnectionProvider connectionProvider;

    	private ListableBeanFactory beanFactory;

    	public StalactiteRepositoryFactory(AdvancedEntityPersister<?, ?> entityPersister,
    									   Dialect dialect,
    									   ConnectionProvider connectionProvider) {
    		this.entityPersister = entityPersister;
    		this.dialect = dialect;
    		this.connectionProvider = connectionProvider;
    	}

    	@Override
    	public <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
    		// because I don't know in which case this method is invoked I throw an exception
    		throw new NotImplementedException("waiting for use case");
    	}

    	@Override
    	protected Object getTargetRepository(RepositoryInformation metadata) {
    		return new SimpleStalactiteRepository<>(entityPersister);
    	}

    	@Override
    	protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
    		return SimpleStalactiteRepository.class;
    	}

    	/**
    	 * Overridden to be capable of accessing the instance, because parent field is private :'(
    	 * @param beanFactory owning BeanFactory (never {@code null}).
    	 */
    	@Override
    	public void setBeanFactory(BeanFactory beanFactory) {
    		this.beanFactory = (ListableBeanFactory) beanFactory;
    	}

    	@Override
    	protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable QueryLookupStrategy.Key key,
    																   QueryMethodEvaluationContextProvider evaluationContextProvider) {

    		switch (Objects.preventNull(key, Key.CREATE_IF_NOT_FOUND)) {
    			case CREATE:
    				return Optional.of(new CreateQueryLookupStrategy<>(entityPersister, dialect));
    			case USE_DECLARED_QUERY:
    				return Optional.of(new FirstMatchingQueryLookupStrategy(
    						new BeanQueryLookupStrategy<>(beanFactory, dialect),
    						new NativeQueryLookupStrategy<>(entityPersister, dialect, connectionProvider)));
    			case CREATE_IF_NOT_FOUND:
    				return Optional.of(new FirstMatchingQueryLookupStrategy(
    						new BeanQueryLookupStrategy<>(beanFactory, dialect),
    						new NativeQueryLookupStrategy<>(entityPersister, dialect, connectionProvider),
    						new CreateQueryLookupStrategy<>(entityPersister, dialect)));
    			default:
    				throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
    		}
    	}

    	/**
    	 * {@link QueryLookupStrategy} to try to detect a declared query first (
    	 * {@link org.springframework.data.jpa.repository.Query}, JPA named query). In case none is found we fall back on
    	 * query creation.
    	 *
    	 * @author Oliver Gierke
    	 * @author Thomas Darimont
    	 */
    	private static class FirstMatchingQueryLookupStrategy implements QueryLookupStrategy {

    		private final Iterable<QueryLookupStrategy> lookupStrategies;

    		/**
    		 * Creates a new {@link FirstMatchingQueryLookupStrategy}.
    		 *
    		 * @param lookupStrategies must not be {@literal null}.
    		 */
    		public FirstMatchingQueryLookupStrategy(QueryLookupStrategy... lookupStrategies) {
    			this.lookupStrategies = Arrays.asSet(lookupStrategies);
    		}

    		@Override
    		public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) {
    			return Iterables.find(lookupStrategies, lookupStrategy -> lookupStrategy.resolveQuery(method, metadata, factory, namedQueries), java.util.Objects::nonNull).getRight();
    		}
    	}
    }